############################################
# SPDX-License-Identifier: MIT             #
# Copyright (C) 2021-.... Jing Leng        #
# Contact: Jing Leng <lengjingzju@163.com> #
############################################

ifeq ($(ENV_TOP_DIR), )
$(error Please souce env first.)
endif

########## Required host tools ##########
# binutils: texinfo
# glibc: gawk
#########################################

FETCH_SCRIPT    := $(ENV_TOOL_DIR)/fetch_package.sh
PATCH_SCRIPT    := $(ENV_TOOL_DIR)/exec_patch.sh
CACHE_SCRIPT    := $(ENV_TOOL_DIR)/process_cache.sh
MACHINE_SCRIPT  := $(ENV_TOOL_DIR)/process_machine.sh
MAKES           := make $(ENV_BUILD_JOBS)

GMP_VER         ?= 6.2.1
MPFR_VER        ?= 4.2.0
MPC_VER         ?= 1.3.1
ISL_VER         ?= 0.25
LINUX_VER       ?= $(shell $(MACHINE_SCRIPT) linux_version)
BINUTILS_VER    ?= 2.40
GCC_VER         ?= $(shell $(MACHINE_SCRIPT) gcc_version)
GLIBC_VER       ?= 2.36
GDB_VER         ?= 13.1

GMP_URL         := http://ftp.gnu.org/gnu/gmp/gmp-$(GMP_VER).tar.xz
MPFR_URL        := http://ftp.gnu.org/gnu/mpfr/mpfr-$(MPFR_VER).tar.xz
MPC_URL         := http://ftp.gnu.org/gnu/mpc/mpc-$(MPC_VER).tar.gz
ISL_URL         := http://libisl.sourceforge.io/isl-$(ISL_VER).tar.xz
LINUX_URL       := http://cdn.kernel.org/pub/linux/kernel/v$(shell echo $(LINUX_VER) | cut -d '.' -f 1).x/linux-$(LINUX_VER).tar.xz
BINUTILS_URL    := http://ftp.gnu.org/gnu/binutils/binutils-$(BINUTILS_VER).tar.xz
GCC_URL         := http://ftp.gnu.org/gnu/gcc/gcc-$(GCC_VER)/gcc-$(GCC_VER).tar.xz
GLIBC_URL       := http://ftp.gnu.org/gnu/glibc/glibc-$(GLIBC_VER).tar.xz
GDB_URL         := http://ftp.gnu.org/gnu/gdb/gdb-$(GDB_VER).tar.xz

CROSS_TARGET     = $(shell $(MACHINE_SCRIPT) cross_target)
CROSS_DIR        = $(shell $(MACHINE_SCRIPT) toolchain_dir)
LINUX_ARCH       = $(shell $(MACHINE_SCRIPT) linux_arch)

OUT_PATH        ?= $(ENV_OUT_HOST)/toolchain/$(CROSS_DIR)
SRCS_PATH        = $(OUT_PATH)/srcs
OBJS_PATH        = $(OUT_PATH)/objs

CROSS_OUTPATH    = $(shell $(MACHINE_SCRIPT) toolchain_path)
CROSS_SYSROOT    = $(CROSS_OUTPATH)/$(CROSS_DIR)
GLIBC_SYSROOT    = $(CROSS_SYSROOT)/$(CROSS_TARGET)/libc
HOST_SYSROOT     = $(CROSS_SYSROOT)/host

CACHE_APPENDS    = \
	$(CONFIG_ENABLE_LANGUAGES) \
	$(GCC_COMMON_OPTIONS) $(GCC_EXTRA_OPTIONS) \
	$(BINUTILS_COMMON_OPTIONS) $(BINUTILS_EXTRA_OPTIONS) \
	$(GLIBC_COMMON_OPTIONS) $(GLIBC_EXTRA_OPTIONS) \
	$(GDB_COMMON_OPTIONS) $(GDB_EXTRA_OPTIONS)

define do_cache
	$(CACHE_SCRIPT) -m $1 -p $(CROSS_DIR) -n -o $(CROSS_OUTPATH) -i $(CROSS_SYSROOT) -g 2 -v 0 -d none \
		-c $(shell pwd)/Makefile -a '$(CACHE_APPENDS)'
endef

.PHONY: all clean build-all build-depends fetch-all \
	fetch-gmp fetch-mpfr fetch-mpc fetch-isl \
	fetch-linux fetch-binutils fetch-gcc fetch-glibc fetch-gdb \
	build-gmp build-mpfr build-mpc build-isl \
	build-binutils-initial build-gcc-initial \
	build-linux-headers build-glibc \
	build-binutils build-gcc build-gdb \
	install-append

all:
	@set -e
	@checksum=$$($(call do_cache,check)); \
	matchflag=$$(echo "$${checksum}" | grep -wc MATCH); \
	errorflag=$$(echo "$${checksum}" | grep -c ERROR); \
	checkinfo=$$(echo "$${checksum}" | sed '/MATCH/ d'); \
	if [ ! -z "$${checkinfo}" ]; then \
		echo "$${checkinfo}"; \
	fi; \
	if [ $${matchflag} -ne 0 ]; then \
		$(call do_cache,pull); \
		echo "Use $(CROSS_DIR) Cache in $(ENV_CACHE_DIR)."; \
	elif [ $${errorflag} -ne 0 ]; then \
		exit 1; \
	else \
		make build-all && \
		$(call do_cache,push); \
		echo "Push $(CROSS_DIR) Cache to $(ENV_CACHE_DIR)."; \
	fi
	@echo "Build $(CROSS_DIR) Done."
	@set +e

clean:
	@rm -rf $(CROSS_SYSROOT) $(OUT_PATH)
	@echo "Clean $(CROSS_DIR) Done."

build-all: | fetch-all build-depends \
	build-binutils-initial build-gcc-initial \
	build-linux-headers build-glibc \
	build-binutils build-gcc build-gdb \
	install-append

########## Fetch packages ##########

fetch-all: fetch-gmp fetch-mpfr fetch-mpc fetch-isl \
	fetch-linux fetch-binutils fetch-gcc fetch-glibc fetch-gdb

fetch-gmp:
	$(FETCH_SCRIPT) tar "$(GMP_URL)" gmp-$(GMP_VER).tar.xz $(SRCS_PATH) gmp-$(GMP_VER)

fetch-mpfr:
	$(FETCH_SCRIPT) tar "$(MPFR_URL)" mpfr-$(MPFR_VER).tar.xz $(SRCS_PATH) mpfr-$(MPFR_VER)

fetch-mpc:
	$(FETCH_SCRIPT) tar "$(MPC_URL)" mpc-$(MPC_VER).tar.gz $(SRCS_PATH) mpc-$(MPC_VER)
	$(PATCH_SCRIPT) patch patch/mpc $(SRCS_PATH)/mpc-$(MPC_VER)

fetch-isl:
	$(FETCH_SCRIPT) tar "$(ISL_URL)" isl-$(ISL_VER).tar.xz $(SRCS_PATH) isl-$(ISL_VER)

fetch-linux:
	$(FETCH_SCRIPT) tar "$(LINUX_URL)" linux-$(LINUX_VER).tar.xz $(SRCS_PATH) linux-$(LINUX_VER)

fetch-binutils:
	$(FETCH_SCRIPT) tar "$(BINUTILS_URL)" binutils-$(BINUTILS_VER).tar.xz $(SRCS_PATH) binutils-$(BINUTILS_VER)

fetch-gcc:
	$(FETCH_SCRIPT) tar "$(GCC_URL)" gcc-$(GCC_VER).tar.xz $(SRCS_PATH) gcc-$(GCC_VER)
	sed -i 's@print-multi-os-directory@print-multi-directory@g' \
		`find $(SRCS_PATH)/gcc-$(GCC_VER) -name configure -o -name configure.ac -o -name Makefile.in | xargs`

fetch-glibc:
	$(FETCH_SCRIPT) tar "$(GLIBC_URL)" glibc-$(GLIBC_VER).tar.xz $(SRCS_PATH) glibc-$(GLIBC_VER)

fetch-gdb:
	$(FETCH_SCRIPT) tar "$(GDB_URL)" gdb-$(GDB_VER).tar.xz $(SRCS_PATH) gdb-$(GDB_VER)

########## Build dependency libraries ##########

build-depends: | build-gmp build-mpfr build-mpc build-isl

build-gmp:
	mkdir -p $(OBJS_PATH)/gmp && cd $(OBJS_PATH)/gmp; \
		$(SRCS_PATH)/gmp-$(GMP_VER)/configure --prefix=$(HOST_SYSROOT) --disable-shared && \
		$(MAKES) && $(MAKES) install

build-mpfr:
	mkdir -p $(OBJS_PATH)/mpfr && cd $(OBJS_PATH)/mpfr; \
		$(SRCS_PATH)/mpfr-$(MPFR_VER)/configure --prefix=$(HOST_SYSROOT) --disable-shared \
			--with-gmp=$(HOST_SYSROOT) && \
		$(MAKES) && $(MAKES) install

build-mpc:
	mkdir -p $(OBJS_PATH)/mpc && cd $(OBJS_PATH)/mpc; \
		$(SRCS_PATH)/mpc-$(MPC_VER)/configure --prefix=$(HOST_SYSROOT) --disable-shared \
			--with-gmp=$(HOST_SYSROOT) --with-mpfr=$(HOST_SYSROOT) && \
		$(MAKES) && $(MAKES) install

build-isl:
	mkdir -p $(OBJS_PATH)/isl && cd $(OBJS_PATH)/isl; \
		$(SRCS_PATH)/isl-$(ISL_VER)/configure --prefix=$(HOST_SYSROOT) --disable-shared \
			--with-gmp-prefix=$(HOST_SYSROOT) && \
		$(MAKES) && $(MAKES) install

########## Configure toolchain  ##########

TOOLCHAIN_COMMON_OPTIONS = \
	--target=$(CROSS_TARGET) \
	--prefix=$(CROSS_SYSROOT) \
	--with-gmp=$(HOST_SYSROOT) \
	--with-mpfr=$(HOST_SYSROOT) \
	--with-mpc=$(HOST_SYSROOT) \
	--with-isl=$(HOST_SYSROOT)

CONFIG_ENABLE_LANGUAGES ?= c,c++
GCC_COMMON_OPTIONS      += $(shell $(MACHINE_SCRIPT) gcc_arch_option)

CONFIG_ENABLE_BOOTSTRAP ?= n
ifeq ($(CONFIG_ENABLE_BOOTSTRAP), y)
BINUTILS_COMMON_OPTIONS += --enable-bootstrap
GCC_COMMON_OPTIONS      += --enable-bootstrap
GDB_COMMON_OPTIONS      += --enable-bootstrap
else
BINUTILS_COMMON_OPTIONS += --disable-bootstrap
GCC_COMMON_OPTIONS      += --disable-bootstrap
GDB_COMMON_OPTIONS      += --disable-bootstrap
endif

CONFIG_ENABLE_MULTILIB ?= n
ifeq ($(CONFIG_ENABLE_MULTILIB), y)
BINUTILS_COMMON_OPTIONS += --enable-multilib
GCC_COMMON_OPTIONS      += --enable-multilib
GDB_COMMON_OPTIONS      += --enable-multilib
else
BINUTILS_COMMON_OPTIONS += --disable-multilib
GCC_COMMON_OPTIONS      += --disable-multilib
GDB_COMMON_OPTIONS      += --disable-multilib
endif

CONFIG_ENABLE_MULTIARCH ?= y
ifeq ($(CONFIG_ENABLE_MULTIARCH), y)
GCC_COMMON_OPTIONS      += --enable-multiarch
GLIBC_COMMON_OPTIONS    += --enable-multi-arch
GDB_COMMON_OPTIONS      += gl_cv_c_multiarch=yes
else
GCC_COMMON_OPTIONS      += --disable-multiarch
GLIBC_COMMON_OPTIONS    += --disable-multi-arch
GDB_COMMON_OPTIONS      += gl_cv_c_multiarch=no
endif

GCC_EXTRA_OPTIONS       ?= \
	--enable-nls \
	--without-included-gettext \
	--enable-clocale=gnu \
	--enable-lto \
	--enable-linker-build-id \
	--enable-gnu-unique-object \
	--enable-libstdcxx-debug \
	--enable-libstdcxx-time=yes

GDB_EXTRA_OPTIONS       ?= \
	--enable-lto \

########## Build binutils / gcc without glibc  ##########

build-binutils-initial:
	mkdir -p $(OBJS_PATH)/binutils-initial && cd $(OBJS_PATH)/binutils-initial; \
		CC_FOR_BUILD=gcc \
		$(SRCS_PATH)/binutils-$(BINUTILS_VER)/configure \
			$(TOOLCHAIN_COMMON_OPTIONS) \
			$(BINUTILS_COMMON_OPTIONS) \
			&& \
		$(MAKES) && $(MAKES) install

build-gcc-initial:
	mkdir -p $(OBJS_PATH)/gcc-initial && cd $(OBJS_PATH)/gcc-initial; \
		CC_FOR_BUILD=gcc \
		$(SRCS_PATH)/gcc-$(GCC_VER)/configure \
			$(TOOLCHAIN_COMMON_OPTIONS) \
			--enable-languages=c,c++ \
			--with-newlib \
			--without-headers \
			--disable-shared \
			--disable-threads \
			--disable-nls \
			--disable-libatomic \
			$(GCC_COMMON_OPTIONS) \
			&& \
		$(MAKES) all-gcc all-target-libgcc && $(MAKES) install-gcc install-target-libgcc
	cd $(CROSS_SYSROOT)/lib/gcc/$(CROSS_TARGET)/$(GCC_VER) && rm -f libgcc_eh.a && ln -sf libgcc.a libgcc_eh.a

########## Build glibc  ##########

build-linux-headers:
	$(MAKES) ARCH=$(LINUX_ARCH) INSTALL_HDR_PATH=$(GLIBC_SYSROOT)/usr -C $(SRCS_PATH)/linux-$(LINUX_VER) headers_install

build-glibc:
	mkdir -p $(OBJS_PATH)/glibc && cd $(OBJS_PATH)/glibc; \
		export PATH=$(PATH):$(CROSS_SYSROOT)/bin; \
		BUILD_CC=gcc CC=$(CROSS_TARGET)-gcc AR=$(CROSS_TARGET)-ar RANLIB=$(CROSS_TARGET)-ranlib \
		$(SRCS_PATH)/glibc-$(GLIBC_VER)/configure \
			--host=$(CROSS_TARGET) \
			--prefix=/usr \
			--with-binutils=$(CROSS_SYSROOT)/bin \
			--with-headers=$(GLIBC_SYSROOT)/usr/include \
			--enable-shared \
			libc_cv_slibdir=/lib \
			$(GLIBC_COMMON_OPTIONS) \
			$(GLIBC_EXTRA_OPTIONS) \
			&& \
		$(MAKES) && $(MAKES) install_root=$(GLIBC_SYSROOT) install

########## Build binutils / gcc with glibc and gdb ##########

build-binutils:
	mkdir -p $(OBJS_PATH)/binutils && cd $(OBJS_PATH)/binutils; \
		export PATH=$(PATH):$(CROSS_SYSROOT)/bin; \
		CC_FOR_BUILD=gcc \
		$(SRCS_PATH)/binutils-$(BINUTILS_VER)/configure \
			$(TOOLCHAIN_COMMON_OPTIONS) \
			--with-sysroot=$(GLIBC_SYSROOT) \
			--with-build-sysroot=$(GLIBC_SYSROOT) \
			$(BINUTILS_COMMON_OPTIONS) \
			$(BINUTILS_EXTRA_OPTIONS) \
			&& \
		$(MAKES) && $(MAKES) install

build-gcc:
	mkdir -p $(OBJS_PATH)/gcc && cd $(OBJS_PATH)/gcc; \
		export PATH=$(PATH):$(CROSS_SYSROOT)/bin; \
		CC_FOR_BUILD=gcc \
		$(SRCS_PATH)/gcc-$(GCC_VER)/configure \
			$(TOOLCHAIN_COMMON_OPTIONS) \
			--with-sysroot=$(GLIBC_SYSROOT) \
			--with-build-sysroot=$(GLIBC_SYSROOT) \
			--with-toolexeclibdir=$(GLIBC_SYSROOT)/lib \
			--enable-languages=$(CONFIG_ENABLE_LANGUAGES) \
			--enable-shared \
			--enable-threads=posix \
			--enable-checking=release \
			$(GCC_COMMON_OPTIONS) \
			$(GCC_EXTRA_OPTIONS) \
			&& \
		$(MAKES) && $(MAKES) install

build-gdb:
	mkdir -p $(OBJS_PATH)/gdb && cd $(OBJS_PATH)/gdb; \
		export PATH=$(PATH):$(CROSS_SYSROOT)/bin; \
		CC_FOR_BUILD=gcc \
		$(SRCS_PATH)/gdb-$(GDB_VER)/configure \
			$(TOOLCHAIN_COMMON_OPTIONS) \
			--with-libgmp-prefix=$(HOST_SYSROOT) \
			$(GDB_COMMON_OPTIONS) \
			$(GDB_EXTRA_OPTIONS) \
			&& \
		$(MAKES) && $(MAKES) install

install-append:
	rm -rf $(HOST_SYSROOT)
	rm -f $(CROSS_SYSROOT)/bin/$(CROSS_TARGET)-c++
	ln -sf $(CROSS_TARGET)-g++ $(CROSS_SYSROOT)/bin/$(CROSS_TARGET)-c++
	rm -f $(CROSS_SYSROOT)/bin/$(CROSS_TARGET)-gcc-$(GCC_VER)
	ln -sf $(CROSS_TARGET)-gcc $(CROSS_SYSROOT)/bin/$(CROSS_TARGET)-gcc-$(GCC_VER)
	rm -f $(CROSS_SYSROOT)/bin/$(CROSS_TARGET)-ld.bfd
	ln -sf $(CROSS_TARGET)-ld $(CROSS_SYSROOT)/bin/$(CROSS_TARGET)-ld.bfd
	rm -f $(CROSS_SYSROOT)/$(CROSS_TARGET)/bin/ld.bfd
	ln -sf ld $(CROSS_SYSROOT)/$(CROSS_TARGET)/bin/ld.bfd

